package org.zecontech.extraplus.init;

import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.food.Foods;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.FlowerPotBlock;
import net.minecraft.world.level.block.RotatedPillarBlock;
import net.minecraft.world.level.block.SaplingBlock;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.TintedParticleLeavesBlock;
import net.minecraft.world.level.block.grower.TreeGrower;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.material.MapColor;
import net.minecraft.world.level.material.PushReaction;
import net.neoforged.neoforge.registries.DeferredBlock;
import net.neoforged.neoforge.registries.DeferredItem;
import net.neoforged.neoforge.registries.DeferredRegister;
import org.zecontech.extraplus.block.CustomCropBlock;
import org.zecontech.extraplus.block.HerbBlock;
import org.zecontech.extraplus.datagen.levelgen.ModConfiguredFeatureProvider;
import org.zecontech.extraplus.util.Helpers;

import java.util.Optional;
import java.util.function.Function;

import static org.zecontech.extraplus.ExtraPlus.MOD_ID;

/**
 * 模组方块
 */
public class ModBlocks {

    // 模组方块注册总线
    public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(MOD_ID);

    // 矿物和矿石集合方块
    public static final DeferredBlock<Block> RUBY_ORE =
            registerBlock("ruby_ore", Block.Properties.ofFullCopy(Blocks.EMERALD_ORE));
    public static final DeferredBlock<Block> RUBY_BLOCK =
            registerBlock("ruby_block", Block.Properties.ofFullCopy(Blocks.EMERALD_BLOCK));
    public static final DeferredBlock<Block> AMBER_ORE =
            registerBlock("amber_ore", Block.Properties.ofFullCopy(Blocks.EMERALD_ORE));
    public static final DeferredBlock<Block> AMBER_BLOCK =
            registerBlock("amber_block", Block.Properties.ofFullCopy(Blocks.EMERALD_BLOCK));
    public static final DeferredBlock<Block> SILVER_BLOCK =
            registerBlock("silver_block", Block.Properties.ofFullCopy(Blocks.EMERALD_BLOCK));

    // 建筑方块
    public static final DeferredBlock<Block> ICE_BRICKS =
            registerBlock("ice_bricks", Block.Properties.ofFullCopy(Blocks.STONE_BRICKS)
                    .sound(SoundType.GLASS).mapColor(MapColor.ICE));
    public static final DeferredBlock<Block> SNOW_BRICKS =
            registerBlock("snow_bricks", Block.Properties.ofFullCopy(Blocks.STONE_BRICKS)
                    .sound(SoundType.GLASS).mapColor(MapColor.SNOW));

    // 草药方块
    public static final DeferredBlock<Block> PLANTAGO =
            registerHerb("plantago",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_PLANTAGO =
            registerFlowerPot("potted_plantago", PLANTAGO);
    public static final DeferredBlock<Block> ACHILLEA_WHITE =
            registerHerb("achillea_white",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_ACHILLEA_WHITE =
            registerFlowerPot("potted_achillea_white", ACHILLEA_WHITE);
    public static final DeferredBlock<Block> ANAGALLIS_YELLOW =
            registerHerb("anagallis_yellow",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_ANAGALLIS_YELLOW =
            registerFlowerPot("potted_anagallis_yellow", ANAGALLIS_YELLOW);
    public static final DeferredBlock<Block> ANTIRRHINUM =
            registerHerb("antirrhinum",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_ANTIRRHINUM =
            registerFlowerPot("potted_antirrhinum", ANTIRRHINUM);
    public static final DeferredBlock<Block> CAMPANULA_BLUE =
            registerHerb("campanula_blue",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_CAMPANULA_BLUE =
            registerFlowerPot("potted_campanula_blue", CAMPANULA_BLUE);
    public static final DeferredBlock<Block> CENTAUREA =
            registerHerb("centaurea",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_CENTAUREA =
            registerFlowerPot("potted_centaurea", CAMPANULA_BLUE);
    public static final DeferredBlock<Block> DIGITALIS_BLUE =
            registerHerb("digitalis_blue",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_DIGITALIS_BLUE =
            registerFlowerPot("potted_digitalis_blue", DIGITALIS_BLUE);
    public static final DeferredBlock<Block> DIGITALIS_YELLOW =
            registerHerb("digitalis_yellow",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_DIGITALIS_YELLOW =
            registerFlowerPot("potted_digitalis_yellow", DIGITALIS_YELLOW);
    public static final DeferredBlock<Block> DOSERA =
            registerHerb("dosera",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_DOSERA =
            registerFlowerPot("potted_dosera", DOSERA);
    public static final DeferredBlock<Block> IRIS =
            registerHerb("iris",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_IRIS =
            registerFlowerPot("potted_iris", IRIS);
    public static final DeferredBlock<Block> LEONTOPODIUM_WHITE =
            registerHerb("leontopodium_white",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_LEONTOPODIUM_WHITE =
            registerFlowerPot("potted_leontopodium_white", LEONTOPODIUM_WHITE);
    public static final DeferredBlock<Block> LEUCANTHEMUM_WHITE =
            registerHerb("leucanthemum_white",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_LEUCANTHEMUM_WHITE =
            registerFlowerPot("potted_leucanthemum_white", LEUCANTHEMUM_WHITE);
    public static final DeferredBlock<Block> MUSHROOM_AMANITA_GREEN =
            registerHerb("mushroom_amanita_green",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_MUSHROOM_AMANITA_GREEN =
            registerFlowerPot("potted_mushroom_amanita_green", MUSHROOM_AMANITA_GREEN);
    public static final DeferredBlock<Block> MUSHROOM_BOLETUS =
            registerHerb("mushroom_boletus",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_MUSHROOM_BOLETUS =
            registerFlowerPot("potted_mushroom_boletus", MUSHROOM_BOLETUS);
    public static final DeferredBlock<Block> MUSHROOM_CANTHARELLUS =
            registerHerb("mushroom_cantharellus",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_MUSHROOM_CANTHARELLUS =
            registerFlowerPot("potted_mushroom_cantharellus", MUSHROOM_CANTHARELLUS);
    public static final DeferredBlock<Block> MUSHROOM_GALERINA =
            registerHerb("mushroom_galerina",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_MUSHROOM_GALERINA =
            registerFlowerPot("potted_mushroom_galerina", MUSHROOM_GALERINA);
    public static final DeferredBlock<Block> MUSHROOM_GYROMITRA =
            registerHerb("mushroom_gyromitra",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_MUSHROOM_GYROMITRA =
            registerFlowerPot("potted_mushroom_gyromitra", MUSHROOM_GYROMITRA);
    public static final DeferredBlock<Block> MUSHROOM_MACROLEPIOTA =
            registerHerb("mushroom_macrolepiota",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_MUSHROOM_MACROLEPIOTA =
            registerFlowerPot("potted_mushroom_macrolepiota", MUSHROOM_MACROLEPIOTA);
    public static final DeferredBlock<Block> PAPVER_RED =
            registerHerb("papver_red",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_PAPVER_RED =
            registerFlowerPot("potted_papver_red", PAPVER_RED);
    public static final DeferredBlock<Block> TRIFOLIUM_RED =
            registerHerb("trifolium_red",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_TRIFOLIUM_RED =
            registerFlowerPot("potted_trifolium_red", TRIFOLIUM_RED);
    public static final DeferredBlock<Block> TRIFOLIUM_WHITE =
            registerHerb("trifolium_white",
                    MobEffects.SATURATION, 0.35F);
    public static final DeferredBlock<Block> POTTED_TRIFOLIUM_WHITE =
            registerFlowerPot("potted_trifolium_white", TRIFOLIUM_WHITE);

    // 作物方块
    public static final DeferredBlock<Block> ONION =
            registerCustomCropWithoutCustomSeeds("onion", Foods.POTATO, true);
    public static final DeferredBlock<Block> ETHEREAL_STRAWBERRY =
            registerCustomCropWithoutCustomSeeds("ethereal_strawberry", Foods.POTATO, true);
    public static final DeferredBlock<Block> PEPPER =
            registerCustomCropWithoutCustomSeeds("pepper", Foods.SWEET_BERRIES, true);

    // 树木相关
    public static final DeferredBlock<Block> WILLOW_LEAVES =
            registerLeavesBlock("willow_leaves");
    public static final DeferredBlock<Block> WILLOW_LOG =
            registerLogBlock("willow_log", MapColor.COLOR_BROWN, MapColor.COLOR_BROWN);
    public static final DeferredBlock<Block> WILLOW_PLANKS =
            registerBlock("willow_planks",
                    Block::new, planksProperties(MapColor.COLOR_BROWN));
    public static final DeferredBlock<Block> WILLOW_WOOD =
            registerWoodBlock("willow_wood", MapColor.COLOR_BROWN);
    public static final DeferredBlock<Block> WILLOW_SAPLING =
            registerSaplingBlock("willow_sapling", ModConfiguredFeatureProvider.WILLOW);
    public static final DeferredBlock<Block> POTTED_WILLOW_SAPLING =
            registerFlowerPot("potted_willow_sapling", WILLOW_SAPLING);

    /**
     * 注册树干方块
     * @param name 树干名字
     * @param sideColor 侧颜色
     * @param topColor 上部颜色
     * @return 树干方块
     */
    private static DeferredBlock<Block> registerLogBlock(String name, MapColor sideColor, MapColor topColor) {
        return registerBlock(name, RotatedPillarBlock::new,
                logProperties(sideColor, topColor, SoundType.WOOD));
    }

    /**
     * 注册树苗方块
     * @param name 树苗名字
     * @param tree 树苗对应的树
     * @return 树苗方块
     */
    private static DeferredBlock<Block> registerSaplingBlock(String name, ResourceKey<ConfiguredFeature<?, ?>> tree) {
        return registerBlock(name, properties -> new SaplingBlock(new TreeGrower(name, Optional.empty(), Optional.of(tree), Optional.empty()), properties),
                BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).noCollission().randomTicks()
                        .instabreak().sound(SoundType.GRASS).pushReaction(PushReaction.DESTROY));
    }

    /**
     * 注册树木方块
     * @param name 树木名字
     * @param mapColor 颜色
     * @return 树木方块
     */
    private static DeferredBlock<Block> registerWoodBlock(String name, MapColor mapColor) {
        return registerBlock(name,RotatedPillarBlock::new,
                woodProperties(mapColor));
    }

    /**
     * 注册树叶方块
     * @param name 树叶名字
     * @return 树叶方块
     */
    private static DeferredBlock<Block> registerLeavesBlock(String name) {
        return registerBlock(name, (properties) -> new TintedParticleLeavesBlock(0.01F, properties),
                leavesProperties(SoundType.GRASS));
    }

    /**
     * 树干方块属性
     * @param sideColor 侧颜色
     * @param topColor 上部颜色
     * @param sound 声音
     * @return 树干方块属性
     */
    private static BlockBehaviour.Properties logProperties(MapColor sideColor, MapColor topColor, SoundType sound) {
        return BlockBehaviour.Properties.of().mapColor((properties) -> properties.getValue(RotatedPillarBlock.AXIS) == Direction.Axis.Y ? sideColor : topColor).instrument(NoteBlockInstrument.BASS).strength(2.0F).sound(sound).ignitedByLava();
    }

    /**
     * 木头方块属性
     * @param mapColor  颜色
     * @return 木头方块属性
     */
    private static BlockBehaviour.Properties woodProperties(MapColor mapColor) {
        return BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).strength(2.0F).sound(SoundType.WOOD).ignitedByLava();
    }

    /**
     * 木板方块属性
     * @param color 颜色
     * @return 木板方块属性
     */
    private static BlockBehaviour.Properties planksProperties(MapColor color) {
        return BlockBehaviour.Properties.of().mapColor(color).instrument(NoteBlockInstrument.BASS).strength(2.0F, 3.0F).sound(SoundType.WOOD).ignitedByLava();
    }

    /**
     * 树叶方块属性
     * @param sound 声音
     * @return 树叶方块属性
     */
    private static BlockBehaviour.Properties leavesProperties(SoundType sound) {
        return BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).strength(0.2F).randomTicks().sound(sound).noOcclusion().isValidSpawn(Blocks::ocelotOrParrot).isSuffocating(ModBlocks::never).isViewBlocking(ModBlocks::never).ignitedByLava().pushReaction(PushReaction.DESTROY).isRedstoneConductor(ModBlocks::never);
    }

    /**
     * 从不
     * @param blockState 状态
     * @param blockGetter 获取方块
     * @param blockPos 坐标
     * @return 不
     */
    private static boolean never(BlockState blockState, BlockGetter blockGetter, BlockPos blockPos) {
        return false;
    }

    /**
     * 注册自定义作物方块
     * @param name 自定义作物名字
     * @return 自定义作物方块
     */
    private static DeferredBlock<Block> registerCustomCropWithoutCustomSeeds(String name, FoodProperties food, boolean isEdible) {
        var deferredBlock = registerBlock(name, CustomCropBlock::new,
                BlockBehaviour.Properties.ofFullCopy(Blocks.CARROTS)
                        .setId(Helpers.createKey(Registries.BLOCK, name)), false);
        if (isEdible) {
            ModItems.ITEMS.registerItem(name, item -> new BlockItem(deferredBlock.get(), new Item.Properties().useBlockDescriptionPrefix().food(food).setId(Helpers.createKey(Registries.ITEM, name))));
        }else {
            ModItems.ITEMS.registerItem(name, item -> new BlockItem(deferredBlock.get(), new Item.Properties().useBlockDescriptionPrefix().setId(Helpers.createKey(Registries.ITEM, name))));
        }
        return deferredBlock;
    }

    /**
     * 注册自定义作物方块
     * @param name 自定义作物名字
     * @param seedItem 自定义作物种子
     * @return 自定义作物方块
     */
    private static DeferredBlock<Block> registerCustomCropWithoutCustomSeeds(String name, DeferredItem<Item> seedItem) {
        return registerCustomCropWithoutCustomSeeds(name, null, false);
    }

    /**
     * 注册花盆植物方块
     * @param name 花盆植物名字
     * @param plantBlock 植物植物方块
     * @return 花盆植物方块
     * */
    private static DeferredBlock<Block> registerFlowerPot(String name, DeferredBlock<Block> plantBlock) {
        return registerBlock(name,
                properties -> new FlowerPotBlock(plantBlock.get(), properties),
                flowerPotProperties());
    }

    /**
     * 创建花盆方块属性
     * @return 花盆方块属性
     */
    private static BlockBehaviour.Properties flowerPotProperties() {
        return BlockBehaviour.Properties.of().instabreak().noOcclusion().pushReaction(PushReaction.DESTROY);
    }

    /**
     * 注册草药方块
     * @param name 草药名字
     * @param effect 效果
     * @param secs 秒数
     * @return 草药方块
     */
    public static DeferredBlock<Block> registerHerb(String name, Holder<MobEffect> effect, float secs) {
        return registerBlock(name,
                properties -> new HerbBlock(effect, secs, properties),
                BlockBehaviour.Properties.of().mapColor(MapColor.PLANT)
                .noCollission()
                .instabreak().sound(SoundType.GRASS)
                .offsetType(BlockBehaviour.OffsetType.XZ)
                .pushReaction(PushReaction.DESTROY));
    }

    /**
     * 注册方块
     * @param name 方块名字
     * @param blockFactory 方块工厂
     * @param properties 方块属性
     * @param hasBlockItem 是否注册方块物品
     * @return 方块
     */
    public static DeferredBlock<Block> registerBlock(String name, Function<BlockBehaviour.Properties, Block> blockFactory, BlockBehaviour.Properties properties, boolean hasBlockItem) {
        var deferredBlock = BLOCKS.register(name, registryName -> blockFactory.apply(properties.setId(Helpers.createKey(Registries.BLOCK, registryName))));
        if (hasBlockItem) {
            ModItems.ITEMS.register(name, item -> new BlockItem(deferredBlock.get(), new Item.Properties().setId(Helpers.createKey(Registries.ITEM, name)).useBlockDescriptionPrefix()));
        }
        return deferredBlock;
    }

    /**
     * 注册方块
     * @param name 方块名字
     * @param blockFactory 方块工厂
     * @param properties 方块属性
     * @return 方块
     */
    public static DeferredBlock<Block> registerBlock(String name, Function<BlockBehaviour.Properties, Block> blockFactory, BlockBehaviour.Properties properties) {
        return registerBlock(name, blockFactory, properties, true);
    }

    /**
     * 注册方块
     * @param name 方块名字
     * @param properties 方块属性
     * @return 方块
     */
    public static DeferredBlock<Block> registerBlock(String name, BlockBehaviour.Properties properties) {
        return registerBlock(name, Block::new, properties);
    }
}
